package sync

import (
	"container/heap"
	"sync/atomic"

	"gddgame.cc/galaxy/asteroid/cluster"
	"gddgame.cc/galaxy/common/feature/orbit"
	"gddgame.cc/galaxy/common/feature/orbit/sync_protocol"
	"gddgame.cc/galaxy/utils/serialize"
	"github.com/vmihailenco/msgpack"
)

var (
	BlockNumber = int32(0)
)

type blockInstance struct {
	Area  string                   `json:"area"`
	Items map[uint32]*itemInstance `json:"items"`

	Members   map[string]*memberInstance `json:"members"`
	ItemNum   uint32                     `json:"item_num"`
	MemberNum uint32                     `json:"member_num"`
	AvailNum  uint32                     `json:"avail_num"`
	PreNum    uint32                     `json:"pre_num"`

	Times []*timeInstance `json:"times"`

	autoAgent  bool
	connecting bool
	availHeap  memberHeap

	Block   *orbit.BlockLocation
	Service ModeService
}

func NewBlockInstance(area string, block *orbit.BlockLocation, service ModeService, autoAgent bool) *blockInstance {
	b := &blockInstance{
		Area:       area,
		connecting: true,
		availHeap:  make([]*memberInstance, 0, 10),

		Items:     make(map[uint32]*itemInstance, 10),
		Members:   make(map[string]*memberInstance, 10),
		MemberNum: 0,
		ItemNum:   0,

		Times:   make([]*timeInstance, 0, 10),
		Block:   block,
		Service: service,

		autoAgent: autoAgent,
	}
	atomic.AddInt32(&BlockNumber, 1)
	return b
}

func (i *blockInstance) Close() {
	i.connecting = false
	atomic.AddInt32(&BlockNumber, -1)
	orbit.LocationCM.ReleaseBlock(i.Block)
}

// 用于balance的replicate同步
func (i *blockInstance) Marshal() ([]byte, error) {
	return msgpack.Marshal(i)
}
func (i *blockInstance) Unmarshal(b []byte) error {
	if err := msgpack.Unmarshal(b, &i); err != nil {
		return err
	}
	i.connecting = true
	for k, _ := range i.Members {
		member := i.Members[k]
		if member.Avail {
			heap.Push(&i.availHeap, member)
			member.inHeap = true
		} else {
			member.inHeap = false
		}
	}
	return nil
}

func (i *blockInstance) Empty() bool {
	return i.MemberNum == 0 && i.ItemNum == 0 && len(i.Times) == 0
}

func (i *blockInstance) AddItem(hash uint32, item *itemInstance, node cluster.Node) {
	i.Items[hash] = item
	atomic.AddUint32(&i.ItemNum, 1)
	i.calculate()

	agent := item.Owner
	if item.Agent != "" {
		agent = item.Agent
	}
	i.SetAgent(hash, i.Members[agent])
}

func (i *blockInstance) RemoveItem(hash uint32, item *itemInstance) {
	delete(i.Items, hash)
	atomic.StoreUint32(&i.ItemNum, i.ItemNum-1)
	i.calculate()
	i.clean(hash, item.Owner, item.Agent, item.Observer)
}

func (i *blockInstance) SetAgent(hash uint32, member *memberInstance) {
	item := i.Items[hash]
	item.Agent = member.OpenId
	atomic.AddUint32(&member.RelationNum, 1)
	member.Agent.Store(hash, true)
}

func (i *blockInstance) SetObserver(hash uint32, member *memberInstance) {
	item := i.Items[hash]
	item.Observer = member.OpenId
	atomic.AddUint32(&member.RelationNum, 1)
	member.Observer[hash] = true
}

// 找寻代理
func (i *blockInstance) FindAgent() *memberInstance {
	var member *memberInstance

	for {
		//fmt.Println("findAgent")
		if i.availHeap.Len() == 0 {
			return nil
		}
		member = i.availHeap[0]
		if member == nil || !member.Avail {
			heap.Pop(&i.availHeap)
			if member != nil {
				member.inHeap = false
			}
		} else if member.RelationNum > i.PreNum {
			heap.Fix(&i.availHeap, 0)
		} else {
			break
		}
	}
	return member
}

// 获取可靠的观察者
func (i *blockInstance) FindObserver() *memberInstance {
	var member *memberInstance

	// 观察者分配: 如果人数大于1，代理者与观察者可以相同
	//allowAgent := i.MemberNum == 1
	for {
		//fmt.Println("findObserver")
		if i.availHeap.Len() == 0 {
			return nil
		}
		member = i.availHeap[0]
		if member == nil || !member.Avail {
			heap.Pop(&i.availHeap)
			if member != nil {
				member.inHeap = false
			}
		} else if member.RelationNum > i.PreNum {
			heap.Fix(&i.availHeap, 0)
		} else {
			//if !allowAgent && agent == member.OpenId {
			//	heapIndex++
			//
			//	goto Continue
			//}
			break
			//Continue:
			//	continue
		}
	}
	return member
}

func (i *blockInstance) clean(hash uint32, owner string, agent string, observer string) {
	var member *memberInstance
	var ok bool
	member, ok = i.Members[owner]
	if ok {
		delete(member.Owner, hash)
	}
	member, ok = i.Members[agent]
	if ok {
		member.Agent.Delete(hash)
		atomic.StoreUint32(&member.RelationNum, member.RelationNum-1)
	}
	member, ok = i.Members[observer]
	if ok {
		delete(member.Observer, hash)
		atomic.StoreUint32(&member.RelationNum, member.RelationNum-1)
	}
}

func (i *blockInstance) setAvail(openId string, member *memberInstance, node cluster.Node) {
	if member.Avail {
		return
	}
	member.Avail = true
	atomic.AddUint32(&i.AvailNum, 1)
	if !member.inHeap {
		heap.Push(&i.availHeap, member)
		member.inHeap = true
	}
}

// 切换
func (i *blockInstance) resetAvail(openId string, member *memberInstance, node cluster.Node, offline bool) {
	if !i.autoAgent {
		return
	}
	if !member.Avail {
		return
	}
	member.Avail = false
	atomic.StoreUint32(&i.AvailNum, i.AvailNum-1)

	// 替换代理
	agent := i.FindAgent()

	assignAgent := []uint32{}
	if offline {
		// 转移该用户的所有item
		for hash, _ := range member.Owner {
			member.Agent.Delete(hash)
			delete(member.Observer, hash)
			i.SetAgent(hash, agent)
			assignAgent = append(assignAgent, hash)
		}
		member.Agent.Range(func(key, value interface{}) bool {
			hash := key.(uint32)
			i.SetAgent(hash, agent)
			assignAgent = append(assignAgent, hash)
			return true
		})
	} else {
		member.Agent.Range(func(key, value interface{}) bool {
			hash := key.(uint32)
			// 转移非拥有者item
			if _, ok := member.Owner[hash]; ok {
				return true
			}
			i.SetAgent(hash, agent)
			assignAgent = append(assignAgent, hash)
			return true
		})
	}
	i.AssignAgent(node, agent, assignAgent)

	// 替换观察者
	assignObserver := []uint32{}
	observer := i.FindObserver()
	for hash, _ := range member.Observer {
		i.SetObserver(hash, observer)
	}
	i.AssignObserve(node, observer, assignObserver)
	member.Observer = make(map[uint32]bool)
	member.RelationNum = 0
}

func (i *blockInstance) AssignAgent(node cluster.Node, agent *memberInstance, assignAgent []uint32) {
	if len(assignAgent) > 0 {
		data := serialize.GetBag()
		data.Append(sync_protocol.AgentAssignType, assignAgent, agent.OpenId)
		node.Broadcast().Data(data).Send(sync_protocol.Assign)
	}
}

func (i *blockInstance) AssignObserve(node cluster.Node, observer *memberInstance, assignObserver []uint32) {
	if len(assignObserver) > 0 {
		data := serialize.GetBag()
		data.Append(sync_protocol.ObserveAssignType, assignObserver, observer.OpenId)
		node.Broadcast().Data(data).Send(sync_protocol.Assign)
	}
}

func (i *blockInstance) calculate() {
	// 一个item可被assing和observe多次
	//fmt.Println(i.MemberNum)
	if !i.autoAgent || i.MemberNum == 0 {
		return
	}
	i.PreNum = i.ItemNum * 2 / i.MemberNum
}
